Android IPC 通信之 Messenger

说到 IPC 除了我们之前讲的 AIDL ,经常被提及的还有 Messenger ,Messenger 底层是通过 AIDL 来实现的。

其实我第一眼看到这个的时候,心想,Messenger ? Message ? Handler ?

确实,跟他们都有一腿。

再说回来,一般越是上层的封装,越是能简化操作,Messenger 就是为了简化 AIDL 的操作成本的。

而从构造方法上,我们也可以很明显的看出 AIDL 的痕迹。

public Messenger(Handler target){
    mTarget = target.getIMessenger();
}

public Messenger(IBinder target){
    mTarget = IMessenger.Stub.asInterface(target);
}

先说一下使用 Messenger 通信的大致流程吧。

服务端

首先还是要一个 Service ,用来连接,然后创建一个 Handler ,然后在 Handler 的 handleMessage 中处理具体的业务逻辑,再通过这个 Handler 创建 Messenger ,最后在 onBind 中返回 Messenger.getBinder()。

如果想要在收到客户端的消息后再回复一条消息给客户端,可以通过 Message.replyTo 获取到一个 Messenger ,然后用这个 Messenger 发消息就可以了。

客户端

客户端通过跟 Service 绑定 ,获取到 IBinder,然后通过 IBinder 创建一个 Messenger ,后面就可以通过 Messenger.send(Message) 发消息了。

如果想要接收服务端的返回消息,我们还需要一个 Handler ,使用 这个Handler 创建一个 Messenger 。然后在发送消息的时候,把这个 Messenger 赋值给 Message.replyTo 。这样就可以了。

好了,流程知道了,上 Demo !

在这个 Demo ,我们使用的是同一个 APP ,然后把 Service 放到另一个线程。进行跨进程通信。

MessengerService

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";

    private static final int MSG_FROM_CLIENT = 10011;
    private static final int MSG_FROM_SERVICE = 10012;

    public MessengerService() {
    }

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_CLIENT:
                    Log.d(TAG, "receive msg from Client: " + msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null, MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","ok,我收到消息了,稍后回复你。");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger messenger = new Messenger(new MessengerHandler());
    @Override
    public IBinder onBind(Intent intent){
        return messenger.getBinder();
    }
}

现在看起来,比我们之前写 AIDL 的时候清晰了许多,我们只需要一个 Handler 来处理具体的事务,然后用这个 Handler 创建 Messenger ,再把 Messenger.getBinder() 返回就可以了。

记得要注册 Service 。

<service
    android:name=".MessengerService"
    android:process=":remote" />

由于这个 Demo 是在同一个 APP 中进行的,所以可以不用设置 action ,因为在同一个 APP 中,可以直接通过包名全路径进行绑定服务。

客户端

Main2Activity

public class Main2Activity extends AppCompatActivity {

    private static final String TAG = "MessengerActivity";

    private static final int MSG_FROM_CLIENT = 10011;
    private static final int MSG_FROM_SERVICE = 10012;

    private Messenger mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = new Messenger(iBinder);
            Message msg = Message.obtain(null, MSG_FROM_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("msg", "hello ,this is clint");
            msg.setData(bundle);
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_SERVICE:
                    Log.d(TAG, "receive msg from Service: " + msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
}

客户端这边也很简单,直接绑定 Service 后,就直接用 IBinder 创建一个 Messenger 来发消息。为了能接受到服务端的消息,还创建了一个 Handler 和 Messenger 来接收和处理 服务端的消息。

分析

这个部分就有意思了,虽然有点绕,但是看懂了,就有一种好像原本一个塞住的地方被捅开的快感。

其实 Messenger 的源码很简单,只有一百五十行,去掉大部分的注释,估计也就几十行吧。我们还是先从 Messenger 的构造方法开始吧。

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

Messenger 有两个构造函数,一个是通过 Handler 来创建,一个是通过 IBinder 来创建。

我们先看一下传 IBinder 参数的构造方法,IMessenger.Stub.asInterface 这个方法是不是很熟悉? 没错就是我们之前 AIDL 的写法,我们通过这个方法获取到代理对象 Proxy 。现在构造方法把这个代理对象赋值给了 mTarget 这个变量。

mTarget 这个变量定义的时候是一个 IMessenger 类型的变量。之前我们分析 AIDL 的时候,知道 Stub 和 Proxy 都实现了 AIDL 的接口。

我们再看一下 Messenger 发送消息的时候调用的方法 send(msg)

public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

看到这里,我想你大概应该就明白了,这个方法就是使用了 Proxy 代理对象 发送了一个请求。

其实从这里我们也可以推断出 Messenger 使用的 AIDL 接口是 send(message)

Messenger 使用的是下面这个 AIDL 文件。

package android.os;
 
import android.os.Message;
 
/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

那我们就理解了,Messenger 底层也是通过 AIDL 实现的。跟我们之前学 AIDL 的时候一样,也是通过 Proxy 代理对象发送 IPC 请求。

嗯,现在我们知道 Messenger 是怎么发送消息的了,那怎么接受消息呢?

我们从另一个构造方法入手。

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

这里通过 handler.getIMessenger() 获取到一个 IMessenger 。这个是怎么获取到的呢?

final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}

如果已经创建了 mMessenger 了,就直接返回,如果没有就创建一个。

private final class MessengerImpl extends IMessenger.Stub {
    public void send(Message msg) {
        msg.sendingUid = Binder.getCallingUid();
        Handler.this.sendMessage(msg);
    }
}

看到这,我想你又明白了吧,这就是一个 Stub ,我们知道 Stub 也抽象实现了 AIDL 接口,真正实现是在服务端。在这里就已经实现了,然后实现的方式是通过 Handler 发送消息。其实我第一次看到这里是时候,心想,Handler还能跨进程通信?其实不是这样的,我看混了。流程走到这里,已经跨过进程了,现在就只是在进程内使用 Handler 发了个消息。

然后这个消息一发,会发到哪里? 就是发送到我们创建 Messenger 时候使用的 Handler 。然后我们就可以在 Handler 中处理具体的事务了。

最后在 Service 的 onBind 中调用 Messenger.getBinder() 返回 Binder 就可以了。

public IBinder getBinder() {
    return mTarget.asBinder();
}

这个就更加明显了,实际上相当于 Stub.asBinder() 返回的是 Stub 本身。

哦! 原来是这样子。

然后我们在看一下服务端接收到消息后是怎么回复消息的。

我们先在 客户端创建了一个 Handler ,然后使用这个 Handler 创建了一个 Messenger 。最后把这个 Messenger 赋值给 Message.replyTo 这个变量。

服务端要回复的时候就直接拿到这个 Messenger ,然后使用 Messenger.send(msg) 就能发送消息到客户端了。

从我们上面的分析,可以知道,Messenger 的 send 方法调用的是 Handler 内部继承了 Stub 的内部类的 send 方法,然后这个方法具体操作是 使用这个 Handler 发送一个消息。

上面的说法是错误的,这就是像我一开始说的以为 Handler 能够跨进程通信。Handler 不能跨进程通信,而且不同的进程是无法互调的,而是通过 Messenger 发送 IPC 消息,然后 客户端的 Stub 接收到消息,然后使用 Handler 发了个消息。

在这个中间,我有些困惑,通过 Handler 创建的 Messenger ,Messenger 里的 mTarget 其实是 Stub ,而不是 Proxy ,而 Stub 是没有发消息的能力的,实现的 IMessenger 也只是接受到消息后进行处理的方法。我就懵逼了,那这个消息是怎么发送的呢?

我一开始也是想不通,还在群里发了红包,求大佬解惑,结果,有点答非所问,不知道是不是我没有说清楚问题。。。。

后面 debug 的时候发现,在消息发过去之前 mTarget 对象还是 IMessenger 类型,但是到接收的时候就变成 Proxy 了,我就大概知道,应该是传输的时候做了什么处理。

然后翻了翻 Message 和 Messenger 的源码,最后发现,原来是在 Message 在序列化的时候,做了处理。

具体的操作是 Message 在序列化的时候,就传了个 IBinder ,然后接收后进行反序列化的时候,就直接拿这个 IBinder 创建了一个 Messenger ,这样在接收的时候,新创建出来的时候,Messenger 里面的 mTarget 就是 Proxy 了,就可以发消息了。

Message 序列化 和 反序列化 的时候调用的是 Messenger 的静态方法。

Message 序列化 replyTo

Messenger 序列化方法

Messenger 反序列化方法

就这样,服务端拿到 Messenger ,就可以向客户端发消息了。

# IPC  Messenger 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×